#include "../interface/C_gl_Common.h"

#include "../glew/glew.h"

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

C_gl_Common::C_gl_Common(){}
C_gl_Common::~C_gl_Common(){}

bool C_gl_Common::CompileShader(char * pSrcData, 
																int SrcDataLen, 
																char * pFileName, 
																S_api_Shader_Macro * pDefines, 
																I_api_Include * pInclude, 
																char * pFunctionName, 
																char * pProfile, 
																unsigned int Flags, 
																I_api_Blob ** ppShader, 
																I_api_Blob ** ppErrorMsgs)
{
	if (pSrcData != NULL && SrcDataLen != 0)
	{
		return CompileShader(pSrcData, SrcDataLen, pDefines, pFunctionName, pProfile, Flags, ppShader, ppErrorMsgs);
	} 

	if (pFileName != NULL)
	{
		return CompileShader(pFileName, pDefines, pInclude, pFunctionName, pProfile, Flags, ppShader, ppErrorMsgs);
	}

	return false;
}

bool C_gl_Common::CompileShader(char	*	pFileName,
													 S_api_Shader_Macro * pDefines,
													 I_api_Include * pInclude,
													 char * pFunctionName,
													 char * pProfile,
													 unsigned int Flags,
													 I_api_Blob ** ppShader, 
													 I_api_Blob ** ppErrorMsgs)
{
	FILE *pFile = fopen(pFileName, "rb");

	if (pFile == NULL) 
		return false;

	// obtain file size:
	fseek(pFile, 0, SEEK_END);
	int SrcDataLen = ftell (pFile);
	fseek(pFile, 0 , SEEK_SET);

	// allocate memory to contain the whole file:
	char * pSrcData = (char*) malloc (sizeof(char)*SrcDataLen+1);
	if (pSrcData == NULL) 
		return false;

	// copy the file into the buffer:
	size_t nread = fread (pSrcData, 1, SrcDataLen, pFile);
	if (nread != SrcDataLen) 
		return false;

	fclose(pFile);

	pSrcData[SrcDataLen] = 0;
	bool result = CompileShader(pSrcData, SrcDataLen, pDefines, pFunctionName, pProfile, Flags, ppShader, ppErrorMsgs);

	free(pSrcData);

	return result;
}

bool C_gl_Common::CompileShader(char *pSrcData, 
																int SrcDataLen, 
																S_api_Shader_Macro *pDefines, 
																char *pFunctionName, 
																char *pProfile, 
																unsigned int Flags, 
																I_api_Blob **ppShader, 
																I_api_Blob **ppErrorMsgs)
{
	int LogLength = 0;

	int type = 0;

	if (strcmp(pProfile, "vs_5_0") == 0)
		type = GL_VERTEX_SHADER;
	if (strcmp(pProfile, "ps_5_0") == 0)
		type = GL_FRAGMENT_SHADER;
	if (strcmp(pProfile, "gs_5_0") == 0)
		type = GL_GEOMETRY_SHADER_EXT;
	
	if (type == 0) return false;

	int nID = glCreateShader(type);

	SrcDataLen = strlen(pSrcData);
	glShaderSource( nID, 1, (const GLchar **) &pSrcData, (GLint *)&SrcDataLen);

	glCompileShader(nID);

	int32 result = 0;
	glGetObjectParameterivARB(nID, GL_OBJECT_COMPILE_STATUS_ARB, &result);  

	if (!result)
	{
		glGetObjectParameterivARB(nID, GL_OBJECT_INFO_LOG_LENGTH_ARB, &LogLength);
		*ppErrorMsgs = new I_api_Blob(LogLength);
		
		glGetInfoLogARB(nID, LogLength, &LogLength, (char *) (*ppErrorMsgs)->GetBufferPointer());
	}
	else
	{
		*ppShader = new I_api_Blob(sizeof(int));
		memcpy((*ppShader)->GetBufferPointer(), &nID, (*ppShader)->GetBufferSize());
	}

	return result;
}

bool C_gl_Common::CreateInputLayout(S_api_Input_Element_Desc *pInputElementDescs, 
																		int NumElements, 
																		void *pShaderBytecodeWithInputSignature, 
																		int BytecodeLength, 
																		I_api_InputLayout **ppInputLayout)
{
	bool result = false;

	if (pShaderBytecodeWithInputSignature == NULL || BytecodeLength != 4)
		return false;

	int programID = *((int *)pShaderBytecodeWithInputSignature);

	for (int iElement = 0; iElement < NumElements; iElement++)
	{	
		int index = glGetAttribLocation(programID, pInputElementDescs[iElement].SemanticName);

		if (index == -1)
		{
			if (strcmp("pInputElementDescs[iElement].SemanticName", "gl_Vertex") == 0)
				index = E_gl_Reserved_Attribute_Position;
			if (strcmp("pInputElementDescs[iElement].SemanticName", "gl_Normal") == 0)
				index = E_gl_Reserved_Attribute_Normal;
			if (strcmp("pInputElementDescs[iElement].SemanticName", "gl_Color") == 0)
				index = E_gl_Reserved_Attribute_Color;
			if (strcmp("pInputElementDescs[iElement].SemanticName", "gl_MultiTexCoord0") == 0)
				index = E_gl_Reserved_Attribute_MultiTexCoord0;
			if (strcmp("pInputElementDescs[iElement].SemanticName", "gl_MultiTexCoord1") == 0)
				index = E_gl_Reserved_Attribute_MultiTexCoord1;
			if (strcmp("pInputElementDescs[iElement].SemanticName", "gl_MultiTexCoord2") == 0)
				index = E_gl_Reserved_Attribute_MultiTexCoord2;
			if (strcmp("pInputElementDescs[iElement].SemanticName", "gl_MultiTexCoord3") == 0)
				index = E_gl_Reserved_Attribute_MultiTexCoord3;
			
			if (index == -1)
				return false;
			else
				((S_gl_Input_Element_Desc *) pInputElementDescs)[iElement].location = index;
			
			continue;
		}

		int size = 0;
		int type = 0;

		glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_SIZE, &size);
		glGetVertexAttribiv(index, GL_VERTEX_ATTRIB_ARRAY_TYPE, &type);

		((S_gl_Input_Element_Desc *) pInputElementDescs)[iElement].size = size;
		((S_gl_Input_Element_Desc *) pInputElementDescs)[iElement].type = type;
	}

	*ppInputLayout = new C_gl_InputLayout((S_gl_Input_Element_Desc *) pInputElementDescs, NumElements);

	return true;
}